#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <UIKit/UIApplication.h>
#import <SpringBoard/SBApplicationController.h>
#import <SpringBoard/SBApplication.h>
#import <LockInfo/Plugin.h>

#import "framework/FWCommon.h"
#import "framework/FWPlugin.h"
#import "framework/FWApplication.h"

#import "PICommon.h"
#import "PIPlugin.h"
#import "PIIntegration.h"


@implementation PIPlugin

@synthesize appURLScheme, appDisplayIdentifier, appBundleIdentifier, pocketInformantPresent, pocketInformant2, dbEntityKeys;


// ******************************************************************************************
#pragma mark -
#pragma mark Initialisation and object management

- (id)initWithPocketInformant2:(BOOL)pi2
{
    if (self = [super init]) {
        pocketInformant2 = pi2;
    }
    
    return self;
}

- (id)initWithApplication:(FWApplication*)app name:(NSString *)name
{
    if ((self = [super initWithApplication:app name:name])) {
        app.applicationDelegate = self;
        
        //NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
        //[center addObserver:self selector:@selector(update:) name:UIPasteboardChangedNotification object:nil];
    }        
    
    return self;
}

- (void)dealloc
{
    [appBundleIdentifier release];
    [appURLScheme release];
    [appDisplayIdentifier release];
    [dbEntityKeys release];
    
    [super dealloc];
}


// ******************************************************************************************
#pragma mark -
#pragma mark Checking database versions 

static NSInteger const kPI1VersionSQL = 1;
static NSInteger const kPI2VersionSQL = 2;


- (NSObject*)objectForSQLRecord:(sqlite3_stmt*)sqlStatement withId:(NSInteger)sqlid context:(NSObject*)context application:(FWApplication*)application
{
    if (sqlid == kPI1VersionSQL) {
        NSInteger version = sqlite3_column_int(sqlStatement, 0);
        XLog(@"LI:PIPlugin: Detected PI1 database version %i", version);
        return @"OK";
    }
    else if (sqlid == kPI2VersionSQL) {
        NSInteger key = sqlite3_column_int(sqlStatement, 0);
        NSString* name = FWDBReadColumnString(sqlStatement, 1);
        return [NSArray arrayWithObjects:[[NSNumber numberWithInt:key] stringValue], name, nil];
    }
    
    return nil;
}

- (BOOL)testPI1DB:(FWApplication*)application path:(NSString*)path
{
    // Check the database appears to work
    NSString* test = @"select schemaversion from version";
    NSArray* value = FWDBGetRecords(test, kPI1VersionSQL, nil, path, application, self);
    return (value != nil);
}    

- (BOOL)testPI2DB:(FWApplication*)application path:(NSString*)path
{
    // Check the database appears to work
    NSString* test = @"SELECT Z_ENT, Z_NAME FROM Z_PRIMARYKEY";
    NSArray* values = FWDBGetRecords(test, kPI2VersionSQL, nil, path, application, self);
    if (!values)
        return NO;
    
    dbEntityKeys = [[NSMutableDictionary alloc] init];
    for (NSArray* item in values) {
        [dbEntityKeys setValue:[item objectAtIndex:0] forKey:[item objectAtIndex:1]];
    }

    DebugLog(@"LI:PIPlugin: Read PI2 entity keys = %@", dbEntityKeys);

    return [dbEntityKeys objectForKey:kPIEntityTypeTask] && [dbEntityKeys objectForKey:kPIEntityTypeEvent];
}    


// ******************************************************************************************
#pragma mark -
#pragma mark Configuration and data management

static NSString* const kPublicAppRoot = @"/var/mobile";
static NSString* const kPI1DBPath = @"/Documents/pidata.sqlite";
static NSString* const kPI2DBPath = @"/Documents/pim.informantdata";
static NSString* const kPIPrefsPattern = @"/Library/Preferences/%@.plist";


- (BOOL)updateApplication:(FWApplication*)application
{
#ifdef IGNORE_MISSING_APPLICATION
    pocketInformantPresent = NO;
    return NO;
#endif
    
    SBApplication* app = [PIPlugin findPocketInformantForApplication:application];
    if (!app) {
        XLog(@"LI:PIPlugin: No application found - marking PI missing");
        pocketInformantPresent = NO;

#ifdef DEVELOPMENT_MODE
        pocketInformantPresent = YES;
        pocketInformant2 = YES;
#endif
        
        return NO;
    }
    
    self.appURLScheme = app.urlScheme;
    self.appDisplayIdentifier = app.displayIdentifier;
    self.appBundleIdentifier = app.bundleIdentifier;

    // Do we have PI1 or PI2?
    pocketInformant2 = [application.databasePath hasSuffix:kPI2DBPath];
    pocketInformantPresent = NO;
    
    // Check the database appears to work
    if (pocketInformant2) {
        if (![self testPI2DB:application path:application.databasePath])
            XLog(@"LI:PIPlugin: PI2 Database not readable at %@", application.databasePath);
        else
            pocketInformantPresent = YES;
    }
    else {
        if (![self testPI1DB:application path:application.databasePath])
            XLog(@"LI:PIPlugin: PI1 Database not readable at %@", application.databasePath);
        else
            pocketInformantPresent = YES;
    }
    
    if (!pocketInformantPresent) {
        XLog(@"LI:PIPlugin: Not happy with DB - marking PI missing");
        return NO;
    }

#ifdef DEBUG
    // Get initial config
    NSDictionary* prefs = [NSDictionary dictionaryWithContentsOfFile:application.preferencesPath];
    DebugLog(@"LI:PIPlugin: PI Preferences on startup = %@", prefs);
#endif
    
    return YES;
}

+ (BOOL)findDatabaseAtPath:(NSString*)path testPI2:(BOOL)pi2 application:(FWApplication*)application appInfo:(SBApplication*)appInfo
{
    XLog(@"LI:PIPlugin: Searching for database; PI2 = %d; path = %@", pi2, path);

    NSFileManager* fileMgr = [[[NSFileManager alloc] init] autorelease];
    NSString* dbPath = [path stringByAppendingString:pi2 ? kPI2DBPath : kPI1DBPath];
    if ([fileMgr isReadableFileAtPath:dbPath]) {
        // Found database at standard location
        XLog(@"LI:PIPlugin: Database found at standard location %@", dbPath);
        application.databasePath = dbPath;
        application.preferencesPath = [path stringByAppendingFormat:kPIPrefsPattern, appInfo.displayIdentifier];
        return YES;
    }

    dbPath = [kPublicAppRoot stringByAppendingString:pi2 ? kPI2DBPath : kPI1DBPath];
    if ([fileMgr isReadableFileAtPath:dbPath]) {
        // Found database in an odd location!
        XLog(@"LI:PIPlugin: Database unexpectedly found at %@", dbPath);
        application.databasePath = dbPath;
        application.preferencesPath = [kPublicAppRoot stringByAppendingFormat:kPIPrefsPattern, appInfo.displayIdentifier];
        return YES;
    }
    
    // No database found
    XLog(@"LI:PIPlugin: No database found");
    return NO;
}

+ (SBApplication*)findPocketInformantForApplication:(FWApplication*)application
{
    Class cls = objc_getClass("SBApplicationController");
    SBApplicationController* ctr = [cls sharedInstance];

    // Searching for iPhone app
    XLog(@"LI:PIPlugin: Searching for application: %@", PIiPhoneIdentifier);
    SBApplication* app = [ctr applicationWithDisplayIdentifier:PIiPhoneIdentifier];
    
    if (!app) {
        // Searching for PI iPad app
        XLog(@"LI:PIPlugin: Searching for application: %@", PIiPadIdentifier);
        app = [ctr applicationWithDisplayIdentifier:PIiPadIdentifier];
    }
    
    if (!app) {
        XLog(@"LI:PIPlugin: Couldn't find application anywhere!");
        return nil;
    }
    else
        XLog(@"LI:PIPlugin: Discovered application: %@", app);
    
    NSString* path = [app.path stringByDeletingLastPathComponent];
    application.applicationPath = app.path;
    application.preferencesPath = [path stringByAppendingFormat:kPIPrefsPattern, app.displayIdentifier];
    
    // Find the database. This will tell us which version of PI we are using
    if (![PIPlugin findDatabaseAtPath:path testPI2:YES application:application appInfo:app]) {
        if (![PIPlugin findDatabaseAtPath:path testPI2:NO application:application appInfo:app]) {
            XLog(@"LI:PIPlugin: Cannot find database");
            return nil;
        }
    }

    // Set information for the plugin
    application.pluginBundle = application.fwPlugin.liPlugin.bundle;
    NSString* baseName = [[[application.pluginBundle bundlePath] lastPathComponent]
                          stringByReplacingOccurrencesOfString:@".bundle" withString:@".plist"];
    application.pluginPreferencesPath = [kPublicAppRoot stringByAppendingFormat:@"/Library/Preferences/%@", baseName];
    
    XLog(@"LI:PIPlugin: Application path = %@", application.applicationPath);
    XLog(@"LI:PIPlugin: Application preferences path = %@", application.preferencesPath);
    XLog(@"LI:PIPlugin: Application database path = %@", application.databasePath);
    XLog(@"LI:PIPlugin: Application plugin preferences path = %@", application.pluginPreferencesPath);
    XLog(@"LI:PIPlugin: Plugin bundle: %@", application.pluginBundle);
    
    // Record information about the application for the preferences components
    if (UIPasteboard* pasteBoard = [UIPasteboard pasteboardWithName:kPIStatusPasteboard create:YES]) {
        // Do we have PI1 or PI2?
        BOOL pi2 = [application.databasePath hasSuffix:kPI2DBPath];
        
        pasteBoard.persistent = YES;
        pasteBoard.items = [NSArray arrayWithObjects:
                            [NSDictionary dictionaryWithObject:application.databasePath forKey:kPIPasteboardDBPath],
                            [NSDictionary dictionaryWithObject:application.applicationPath forKey:kPIPasteboardAppPath],
                            [NSDictionary dictionaryWithObject:pi2 ? @"2" : @"1" forKey:kPIPasteboardPIVersion],
                           nil];
    }
    
    return app;
}

- (BOOL)application:(FWApplication*)application shouldUpdateForDate:(NSDate*)date
{
    return NO;
}

@end
